package org.zoomdev.zoom.retry;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * 重试服务 - 基于增强版Timer构建
 */
public class RetryService {
    // 核心Timer实例
    private final DriftProofTimer timer;

    // 任务ID生成器
    private final AtomicLong taskIdGenerator = new AtomicLong(0);

    // 任务注册表
    private final Map<Long, RetryTask> registeredTasks = new ConcurrentHashMap<>();

    public RetryService() {
        this.timer = new DriftProofTimer();
    }

    public RetryService(DriftProofTimer timer) {
        this.timer = timer;
    }

    /**
     * 使用示例
     */
    public static void main(String[] args) throws InterruptedException {
        // 创建重试服务
        RetryService retryService = new RetryService();
        retryService.start();

        // 1. 简单重试示例（最多重试3次，每次间隔200ms）
        long simpleTask = retryService.schedule(
                () -> {
                    System.out.println("SimpleRetry task executing...");
                    if (Math.random() > 0.3) {
                        throw new RuntimeException("SimpleRetry failed");
                    }
                },
                1, TimeUnit.SECONDS,
                new SimpleRetryStrategy(200, TimeUnit.MILLISECONDS),
                5L, TimeUnit.SECONDS, // 最大重试时间5秒
                3  // 最大重试次数3次
        );

        // 2. 延时序列重试示例（1s, 5s, 10s）
        long sequenceTask = retryService.schedule(
                () -> {
                    System.out.println("SequenceRetry task executing...");
                    if (Math.random() > 0.4) {
                        throw new RuntimeException("SequenceRetry failed");
                    }
                },
                2, TimeUnit.SECONDS,
                new DelaySequenceRetryStrategy(new long[]{1, 5, 10}, TimeUnit.SECONDS),
                30L, TimeUnit.SECONDS, // 最大重试时间30秒
                null // 无最大重试次数限制
        );

        // 3. 定时重试示例（每隔3秒重试一次）
        long scheduledTask = retryService.scheduleWithFixedDelay(
                () -> {
                    System.out.println("ScheduledRetry task executing...");
                    if (Math.random() > 0.5) {
                        throw new RuntimeException("ScheduledRetry failed");
                    }
                },
                3, 0, TimeUnit.SECONDS, // initialDelay=3s, period=0（不重复）
                new ScheduledRetryStrategy(3, TimeUnit.SECONDS),
                60L, TimeUnit.SECONDS, // 最大重试时间60秒
                10 // 最大重试次数10次
        );

        // 4. 使用Timer的补偿模式
        retryService.getTimer().setDriftControlStrategy(DriftProofTimer.DriftControlStrategy.COMPENSATE_MISSED);

        // 监控任务状态
        ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
        monitor.scheduleAtFixedRate(() -> {
            System.out.println("\n===== Retry Service Status =====");
            System.out.println("Registered tasks: " + retryService.registeredTasks.size());
            System.out.println("Timer pending tasks: " + retryService.getTimer().pendingTasks());
            System.out.println("Next expiration: " +
                    retryService.getTimer().getNextExpirationTime().map(t -> (t - System.currentTimeMillis()) + "ms")
                            .orElse("None"));
        }, 0, 3, TimeUnit.SECONDS);

        // 运行30秒
        Thread.sleep(30000);

        // 停止服务
        System.out.println("\nStopping retry service...");
        retryService.stop();
        monitor.shutdown();

        System.out.println("Service stopped. Program exiting.");
    }

    /**
     * 启动重试服务
     */
    public void start() {
        timer.start();
    }

    /**
     * 停止重试服务
     */
    public void stop() {
        timer.stop();
    }

    /**
     * 获取底层Timer实例
     */
    public DriftProofTimer getTimer() {
        return timer;
    }

    /**
     * 安排一次性任务（带重试策略）
     */
    public long schedule(Runnable task, long delay, TimeUnit unit,
                         RetryStrategy retryStrategy, Long maxRetryDuration, TimeUnit maxUnit,
                         Integer maxRetries) {
        RetryTask retryTask = new RetryTask(task, retryStrategy,
                maxRetryDuration != null ? maxUnit.toMillis(maxRetryDuration) : -1,
                maxRetries);
        return scheduleTask(retryTask, delay, unit, false, -1, null);
    }

    /**
     * 安排周期性任务（带重试策略）
     */
    public long scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit,
                                       RetryStrategy retryStrategy, Long maxRetryDuration, TimeUnit maxUnit,
                                       Integer maxRetries) {
        RetryTask retryTask = new RetryTask(task, retryStrategy,
                maxRetryDuration != null ? maxUnit.toMillis(maxRetryDuration) : -1,
                maxRetries);
        return scheduleTask(retryTask, initialDelay, unit, true, period, unit);
    }

    /**
     * 取消任务
     */
    public void cancel(long taskId) {
        registeredTasks.remove(taskId);
        timer.cancel(taskId);
    }

    /**
     * 检查任务是否已取消
     */
    public boolean isCanceled(long taskId) {
        return timer.isCanceled(taskId);
    }

    /**
     * 设置全局异常处理器
     */
    public void setExceptionHandler(Consumer<Exception> handler) {
        timer.setExceptionHandler(handler);
    }

    /**
     * 安排任务到Timer
     */
    private long scheduleTask(RetryTask retryTask, long delay, TimeUnit unit,
                              boolean periodic, long period, TimeUnit periodUnit) {
        long taskId = taskIdGenerator.incrementAndGet();
        registeredTasks.put(taskId, retryTask);

        // 封装原始任务为重试感知的任务
        Runnable wrappedTask = () -> executeWithRetry(retryTask, taskId);

        // 委托给Timer安排任务
        if (periodic) {
            return timer.scheduleWithFixedDelay(wrappedTask, delay, period, unit);
        } else {
            return timer.schedule(wrappedTask, delay, unit);
        }
    }

    /**
     * 执行任务并处理重试逻辑
     */
    private void executeWithRetry(RetryTask retryTask, long taskId) {
        // 记录初始执行时间
        if (retryTask.firstFailureTime == 0) {
            retryTask.firstFailureTime = System.currentTimeMillis();
        }

        try {
            retryTask.execute();

            // 执行成功，重置重试状态
            retryTask.resetRetryState();
        } catch (Exception e) {
            handleTaskFailure(retryTask, taskId, e);
        }
    }

    /**
     * 处理任务失败情况
     */
    private void handleTaskFailure(RetryTask retryTask, long taskId, Exception e) {
        retryTask.exceptionHandler.accept(e);

        // 检查是否超过最大重试次数
        if (retryTask.exceedsMaxRetries()) {
            System.out.printf("Task %d exceeded max retries (%d). Stopping retries.%n",
                    taskId, retryTask.maxRetries);
            retryTask.retryAborted = true;
            return;
        }

        // 检查是否超过最大重试时间
        if (retryTask.exceedsMaxRetryDuration()) {
            System.out.printf("Task %d exceeded max retry duration (%dms). Stopping retries.%n",
                    taskId, retryTask.maxRetryDuration);
            retryTask.retryAborted = true;
            return;
        }

        // 计算下次重试延迟
        long nextDelay = retryTask.retryStrategy.calculateNextDelay(retryTask.currentRetryCount);
        if (nextDelay < 0) {
            System.out.printf("Task %d has no more retry attempts. Stopping retries.%n", taskId);
            retryTask.retryAborted = true;
            return;
        }

        // 更新重试计数
        retryTask.currentRetryCount++;
        System.out.printf("Scheduling retry #%d for task %d after %dms%n",
                retryTask.currentRetryCount, taskId, nextDelay);

        // 使用Timer调度重试
        timer.schedule(() -> executeWithRetry(retryTask, taskId), nextDelay, TimeUnit.MILLISECONDS);
    }

    /**
     * 重试策略接口
     */
    public interface RetryStrategy {
        /**
         * 计算下一次重试的延迟
         *
         * @param currentRetry 当前重试次数（从0开始）
         * @return 下一次重试的延迟时间（毫秒）
         */
        long calculateNextDelay(int currentRetry);
    }

    /**
     * 重试任务包装类
     */
    private static class RetryTask {
        private final Runnable task;
        private final RetryStrategy retryStrategy;
        private final long maxRetryDuration; // 最大重试时间(ms)
        private final Integer maxRetries;    // 最大重试次数
        private long firstFailureTime;        // 第一次失败时间戳(ms)
        private int currentRetryCount;        // 当前重试次数
        private boolean retryAborted;        // 是否终止重试
        private Consumer<Exception> exceptionHandler = e -> {
        };

        public RetryTask(Runnable task, RetryStrategy retryStrategy,
                         long maxRetryDuration, Integer maxRetries) {
            this.task = task;
            this.retryStrategy = retryStrategy;
            this.maxRetryDuration = maxRetryDuration;
            this.maxRetries = maxRetries;
        }

        public void execute() {
            task.run();
        }

        public void resetRetryState() {
            firstFailureTime = 0;
            currentRetryCount = 0;
            retryAborted = false;
        }

        public boolean exceedsMaxRetryDuration() {
            if (maxRetryDuration <= 0) return false;
            return firstFailureTime > 0 &&
                    (System.currentTimeMillis() - firstFailureTime) > maxRetryDuration;
        }

        public boolean exceedsMaxRetries() {
            if (maxRetries == null) return false;
            return currentRetryCount >= maxRetries;
        }

        public void setExceptionHandler(Consumer<Exception> handler) {
            this.exceptionHandler = handler;
        }
    }

    /**
     * 简单重试策略（固定延迟）
     */
    public static class SimpleRetryStrategy implements RetryStrategy {
        private final long fixedDelay; // 固定延迟(ms)

        public SimpleRetryStrategy(long delay, TimeUnit unit) {
            this.fixedDelay = unit.toMillis(delay);
        }

        @Override
        public long calculateNextDelay(int currentRetry) {
            return fixedDelay; // 总是返回固定延迟
        }
    }

    /**
     * 延时序列重试策略
     */
    public static class DelaySequenceRetryStrategy implements RetryStrategy {
        private final long[] delays; // 延迟序列(ms)

        public DelaySequenceRetryStrategy(long[] delays, TimeUnit unit) {
            this.delays = Arrays.copyOf(delays, delays.length);
            for (int i = 0; i < this.delays.length; i++) {
                this.delays[i] = unit.toMillis(delays[i]);
            }
        }

        public DelaySequenceRetryStrategy(List<Long> delays, TimeUnit unit) {
            this.delays = delays.stream().mapToLong(unit::toMillis).toArray();
        }

        @Override
        public long calculateNextDelay(int currentRetry) {
            if (currentRetry >= delays.length) {
                return -1; // 超出序列长度
            }
            return delays[currentRetry];
        }
    }

    /**
     * 定时重试策略（固定间隔）
     */
    public static class ScheduledRetryStrategy implements RetryStrategy {
        private final long fixedInterval; // 固定间隔(ms)

        public ScheduledRetryStrategy(long interval, TimeUnit unit) {
            this.fixedInterval = unit.toMillis(interval);
        }

        @Override
        public long calculateNextDelay(int currentRetry) {
            return fixedInterval; // 总是返回固定间隔
        }
    }
}